
# (1) 定义：用一系列函数解决问题。

# -- 函数可以赋值给变量，赋值后变量绑定函数。

# -- 允许将函数作为参数传入另一个函数。

# -- 允许函数返回一个函数。

# (2) 高阶函数：将函数作为参数或返回值的函数。


# 5.1 函数作为参数

# 将核心逻辑传入方法体，
# 使该方法的适用性更广，体现了面向对象的开闭原则。


# 普通函数

list01 = [342, 4, 54, 56, 6776]


def get_num_gt_100():
    '''
    定义函数,在列表中查找第一个大于100的数
    '''
    for num in list01:
        if num > 100:
            return num


def get_num_by_even():
    '''
    定义函数,在列表中查找第一个偶数
    '''
    for num in list01:
        if num % 2 == 0:
            return num


num_gt100 = get_num_gt_100()
print(num_gt100)  # 342

num_by_even = get_num_by_even()
print(num_by_even)  # 342


# 通用函数
def find_single(condition):
    for item in list01:
        if condition(item):
            return item


# 作为参数的函数

def condition01(num):
    return num > 100


def condition02(num):
    return num % 2 == 0


def condition03(num):
    return num < 100


print(find_single(condition01))
# 342
print(find_single(condition02))
# 342
print(find_single(condition03))
# 4


# 练习2：

# 需求：

# 定义函数，在员工列表中查找所有部门是9001的员工

# 定义函数，在员工列表中查找所有姓名是2个字的员工

# 步骤：

# ​ -- 根据需求，写出函数。

# ​ -- 因为主体逻辑相同,核心算法不同.

# ​ 所以使用函数式编程思想(分、隔、做)

# ​ 创建通用函数find_all

# -- 在当前模块中调用


# 数据源

class Employee:

    def __init__(self, eid, did, name, money):
        self.eid = eid  # 员工编号
        self.did = did  # 部门编号
        self.name = name
        self.money = money


list_employees = [
    Employee(1001, 9002, "师父", 60000),
    Employee(1002, 9001, "孙悟空", 50000),
    Employee(1003, 9002, "猪八戒", 20000),
    Employee(1004, 9001, "沙僧", 30000),
    Employee(1005, 9001, "小白龙", 15000),
]

# 普通函数

# 定义函数，在员工列表中查找所有部门是9001的员工


def find_by_did(did):
    # emp_obj_list = []
    # for emp_obj in list_employees:
    #     if emp_obj.did == did:
    #         emp_obj_list.append(emp_obj)
    # return emp_obj_list
    return [emp_obj for emp_obj in list_employees if emp_obj.did == did]


print([emp_obj.name for emp_obj in find_by_did(9001)])
# ['孙悟空', '沙僧', '小白龙']


# 定义函数，在员工列表中查找所有姓名是2个字的员工

def find_by_name(str_num):
    # emp_obj_list = []
    # for emp_obj in list_employees:
    #     if len(emp_obj.name) == str_num:
    #         emp_obj_list.append(emp_obj)
    # return emp_obj_list
    return [emp_obj for emp_obj in list_employees if len(emp_obj.name) == str_num]


print([emp_obj.name for emp_obj in find_by_name(2)])
# ['师父', '沙僧']


# 通用函数


def find_all(condition, **args):
    return [emp_obj for emp_obj in list_employees if condition(emp_obj, args)]


def by_did_condi(obj, condi_ditc):
    return obj.did == condi_ditc["did"]


by_dids = find_all(by_did_condi, did=9001)
print([did_obj.name for did_obj in by_dids])
# ['孙悟空', '沙僧', '小白龙']

by_dids = find_all(by_did_condi, did=9002)
print([did_obj.name for did_obj in by_dids])
# ['师父', '猪八戒']


def by_name_condi(obj, condi_ditc):
    return len(obj.name) == condi_ditc["name_num"]


by_name = find_all(by_name_condi, name_num=2)
print([name_obj.name for name_obj in by_name])
# ['师父', '沙僧']

by_name = find_all(by_name_condi, name_num=3)
print([name_obj.name for name_obj in by_name])
# ['孙悟空', '猪八戒', '小白龙']


# 5.1.1 lambda 表达式
# (1) 定义：是一种匿名方法

# (2) 作用：
# -- 作为参数传递时语法简洁，优雅，代码可读性强。
# -- 随时创建和销毁，减少程序耦合度。

# (3) 语法
# # 定义：
# 变量 = lambda 形参: 方法体
# # 调用：
# 变量(实参)

# (4) 说明：
# -- 形参没有可以不填
# -- 方法体只能有一条语句，且不支持赋值语句。


# 通用函数
# 插值符合条件的可迭代容器特征值
# container 容器
def find_iter_single(iter_obj, condition):
    return [item for item in iter_obj if condition(item)]


list02 = [214, 4, 51, 55, 1254]

result = find_iter_single(list02, lambda num: num > 100)
print(result)
# [214, 1254]

result = find_iter_single(list02, lambda num: num % 2 == 0)
print(result)
# [214, 4, 1254]

result = find_iter_single(list02, lambda num: num < 100)
print(result)
# [4, 51, 55]


# 5.1.2 内置高阶函数

# (1) map（函数，可迭代对象）：
# 使用可迭代对象中的每个元素调用函数，
# 将返回值作为新可迭代对象元素；返回值为新可迭代对象。


# 1. map 映射
# 需求:获取所有员工姓名

for item in map(lambda item: item.name, list_employees):
    print(item)

# 师父
# 孙悟空
# 猪八戒
# 沙僧
# 小白龙


# (2) filter(函数，可迭代对象)：根据条件筛选可迭代对象中的元素，
# 返回值为新可迭代对象。

# 2. filter 过滤器
# 需求：查找所有部门是9002的员工

for item in filter(lambda item: item.did == 9002, list_employees):
    print(item.__dict__)
# {'eid': 1001, 'did': 9002, 'name': '师父', 'money': 60000}
# {'eid': 1003, 'did': 9002, 'name': '猪八戒', 'money': 20000}


# __dict__ 是对象的字典属性


# (4) max(可迭代对象，key = 函数)：
# 根据函数获取可迭代对象的最大值。

# 3. max min 最值
emp = max(list_employees, key=lambda emp: emp.money)
print(emp.__dict__)
# {'eid': 1001, 'did': 9002, 'name': '师父', 'money': 60000}


# (3) sorted(可迭代对象，key = 函数,reverse = bool值)：
# 排序，返回值为排序结果。

# 4. sorted  默认升序
# 升序排列
new_list = sorted(list_employees, key=lambda emp: emp.money)
print([item.__dict__ for item in new_list])
# [{'eid': 1005, 'did': 9001, 'name': '小白龙', 'money': 15000},
#  {'eid': 1003, 'did': 9002, 'name': '猪八戒', 'money': 20000},
# {'eid': 1004, 'did': 9001, 'name': '沙僧', 'money': 30000},
# {'eid': 1002, 'did': 9001, 'name': '孙悟空', 'money': 50000},
#  {'eid': 1001, 'did': 9002, 'name': '师父', 'money': 60000}]

new_list = sorted(list_employees, key=lambda emp: emp.money, reverse=True)
print([item.__dict__ for item in new_list])
# [{'eid': 1001, 'did': 9002, 'name': '师父', 'money': 60000},
#  {'eid': 1002, 'did': 9001, 'name': '孙悟空', 'money': 50000},
#  {'eid': 1004, 'did': 9001, 'name': '沙僧', 'money': 30000},
#  {'eid': 1003, 'did': 9002, 'name': '猪八戒', 'money': 20000},
#  {'eid': 1005, 'did': 9001, 'name': '小白龙', 'money': 15000}]


# 练习数据源

class Commodity:
    def __init__(self, cid=0, name="", price=0):
        self.cid = cid
        self.name = name
        self.price = price


list_commodity_infos = [
    Commodity(1001, "屠龙刀", 10000),
    Commodity(1002, "倚天剑", 10000),
    Commodity(1003, "金箍棒", 52100),
    Commodity(1004, "口罩", 20),
    Commodity(1005, "酒精", 30),
]


# -- 在商品列表，获取所有名称与单价
for item in map(lambda item: f'{item.name}:{item.price}', list_commodity_infos):
    print(item)

# 屠龙刀:10000
# 倚天剑:10000
# 金箍棒:52100
# 口罩:20
# 酒精:30


# -- 在商品列表中，获取所有单价小于10000的商品
for item in filter(lambda item: item.price < 10000, list_commodity_infos):
    print(item.__dict__)
# {'cid': 1004, 'name': '口罩', 'price': 20}
# {'cid': 1005, 'name': '酒精', 'price': 30}


# -- 对商品列表，根据单价进行降序排列
new_list = sorted(list_commodity_infos,
                  key=lambda emp: emp.price, reverse=True)
print([obj.__dict__ for obj in new_list])
# [{'cid': 1003, 'name': '金箍棒', 'price': 52100},
#   {'cid': 1001, 'name': '屠龙刀', 'price': 10000},
#   {'cid': 1002, 'name': '倚天剑', 'price': 10000},
# {'cid': 1005, 'name': '酒精', 'price': 30},
# {'cid': 1004, 'name': '口罩', 'price': 20}]


# -- 获取元组中长度最大的列表 ([1,1],[2,2,2],[3,3,3])

tuple01 = ([1, 1], [2, 2, 2], [3, 3, 3])
# emp = max(tuple01,key=lambda list_obj: len(list_obj))
emp = max(tuple01, key=lambda list_obj: list_obj)
print(emp)
# [3, 3, 3]



# 5.2 函数作为返回值


# 5.2.1 闭包

# (1) 三要素：

# -- 必须有一个内嵌函数。

# -- 内嵌函数必须引用外部函数中变量。

# -- 外部函数返回值必须是内嵌函数。

# (2) 语法

# # 定义：
# def 外部函数名(参数):
#     外部变量
#     def 内部函数名(参数):
#         使用外部变量
#     return 内部函数名

# # 调用：
# 变量 = 外部函数名(参数)
# 变量(参数)

# (4) 优点：内部函数可以使用外部变量。

# (6) 作用：实现python装饰器。


# (7) 演示：

def give_gife_money(money):
    print("获得", money, "元压岁钱")

    def child_buy(commodity, price):
        nonlocal money
        money -= price
        print("购买了", commodity, "花了", price, "元,还剩下", money)
    return child_buy


action = give_gife_money(500)
action("奥特曼", 200)
action("娃娃", 300)
# 获得 500 元压岁钱
# 购买了 奥特曼 花了 200 元,还剩下 300
# 购买了 娃娃 花了 300 元,还剩下 0



# 练习：使用闭包模拟以下情景：

# 在银行开户存入10000

# 购买xx商品花了xx元

# 购买xx商品花了xx元


def set_money(money):
    print("在银行开户存入", money)

    def get_money(comm, buy):
        nonlocal money
        money -= buy
        print(f'购买{comm},商品花了{buy}元,还剩下{money}')
    return get_money


fn = set_money(10000)
fn("手机", 5000)
fn("手表", 3000)
# 在银行开户存入 10000
# 购买手机,商品花了5000元,还剩下5000
# 购买手表,商品花了3000元,还剩下2000




#5.2.2 函数装饰器decorator

# (1) 定义：在不改变原函数的调用以及内部代码情况下，为其添加新功能的函数。

# (2) 语法

# def 函数装饰器名称(func):
#     def wrapper(*args, **kwargs):
#         需要添加的新功能
#         res = func(*args, **kwargs)
#         return res
#     return wrapper

# @ 函数装饰器名称
# def 原函数名称(参数):
#     函数体

# 原函数(参数)

# (3) 本质：使用“@函数装饰器名称”修饰原函数，等同于创建与原函数名称相同的变量，关联内嵌函数；
# 故调用原函数时执行内嵌函数。

# ​ 原函数名称 = 函数装饰器名称（原函数名称）


def func01():
    print("旧功能")
    
def new_func(func):
    def wrapper():
        print("新功能")
        func()
    return wrapper

# func01 = new_func

#本质：使用“@函数装饰器名称”修饰原函数，等同于创建与原函数名称相同的变量，关联内嵌函数；
# 故调用原函数时执行内嵌函数。
func01 = new_func(func01)
func01()
# 新功能
# 旧功能

@new_func
def func02():
    print("旧功能2")
func02()
# 新功能
# 旧功能2



#(3) 装饰器链：
# 一个函数可以被多个装饰器修饰，执行顺序为从近到远。


# 练习1：不改变插入函数与删除函数代码，为其增加验证权限的功能

# def verify_permissions(): 
#     print("验证权限") 

# def insert(): 
#     print("插入")

# def delete():
#     print("删除")

 
def verify_permissions(func): 
    def wrapper():
        print("验证权限")
        func()
    return wrapper

@verify_permissions
def insert(): 
    print("插入")

@verify_permissions
def delete():
    print("删除")
        
insert()
# 验证权限
# 插入
delete()
# 验证权限
# 删除


# 练习2：为sum_data,增加打印函数执行时间的功能.

# ​ 函数执行时间公式： 执行后时间 - 执行前时间


# def sum_data(n):
#     sum_value = 0
#     for number in range(n):
#         sum_value += number
#     return sum_value 

# print(sum_data(10))
# print(sum_data(1000000))


import time

def conut_time(func):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        result = func(*args,**kwargs)
        end_time = time.time()
        print("函数",func.__name__,"运行了",end_time - start_time,"秒")
        return result
    return wrapper

@conut_time
def sum_data(n):
    sum_value = 0
    for number in range(n):
        sum_value += number
    return sum_value
        
print(sum_data(10))
print(sum_data(1000000))




